#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

const int Port = 8888;
pthread_mutex_t g_mutext;

#define MAX_CLIENT_NUM 16

typedef struct _SockInfo {
    struct sockaddr_in addr;
    int fd;
}SockInfo;

SockInfo infos[512];

// static int global_socket_cnt = 0;
static int global_abort_exposure = 0;

void *serveForClient(void * arg);
void startExposure(void *arg);
void abortExposure(void *arg);

int main(void) {
    int sock_fd;
    struct sockaddr_in mysock;
    // struct pthread_data pdata;
    // pthread_t pt;
    socklen_t sin_size = sizeof(struct sockaddr_in);

    // client Info
    int max = sizeof(infos) / sizeof(infos[0]);
    int i;
    for( i=0; i<max; i++ ){
        bzero(&infos[i], sizeof(infos[i]));
        infos[i].fd = -1;
    }


    //初始化socket
    sock_fd = socket(AF_INET,SOCK_STREAM,0);

    int on = 1;                                         // Enable address reuse
    setsockopt( sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );

    //编辑地址信息
    memset(&mysock, 0, sizeof(mysock));
    mysock.sin_family = AF_INET;
    mysock.sin_port = htons(Port);
    mysock.sin_addr.s_addr = INADDR_ANY;

    //绑定地址，然后监听
    bind(sock_fd,(struct sockaddr *)&mysock,sizeof(struct sockaddr));
    if(listen(sock_fd,10) < -1) {
        printf("listen error.\n");
    }

    printf("listening...\n");
    while(1) {
        
        SockInfo *pinfo;
        for(i=0; i<max; i++ ){
            if( infos[i].fd == -1 ){
                pinfo = &infos[i];
                break;
            }
        }
        
        //accpet
        int cfd = accept(sock_fd, (struct sockaddr *)&(pinfo->addr), &sin_size);
        pinfo->fd = cfd;
        printf("**** current sockinfo index: %2d\n", i);

        if( -1 == cfd ) {
            perror("accep\n");
            continue;
        }

        // create new thread
        pthread_t tid;
        pthread_create(&tid, NULL, serveForClient, pinfo);
        pthread_detach(tid);
    }

    close(sock_fd);
    return 0;
}

void *serveForClient(void *arg) {
    
    SockInfo *pinfo = (SockInfo*)arg;

    char ip[32];
    // printf("Client IP: %s, port: %d\n",
    //         inet_ntop(AF_INET, &pinfo->addr.sin_addr.s_addr, ip, sizeof(ip)),
    //         ntohs(pinfo->addr.sin_port));


    int new_fd = pinfo->fd;
    // int socket_cnt = global_socket_cnt;
    char recvbuf[1024];
    char sendbuf[1024] = "recv successfully.\n";

    // fprintf(stdout, "my pthered_id is: %d\n", new_fd);

    while(1) {

        printf("Client IP: %s, port: %d --> ",
            inet_ntop(AF_INET, &pinfo->addr.sin_addr.s_addr, ip, sizeof(ip)),
            ntohs(pinfo->addr.sin_port));

        recv(new_fd, recvbuf, sizeof(recvbuf), 0);
        // fputs(recvbuf,stdout);
        fprintf(stdout, "%s\n", recvbuf);

        if( strcmp(recvbuf, "exposure\n") == 0 ){
            
            global_abort_exposure = 0;
            
            strcpy(sendbuf, "expostarted\n");
            send(new_fd, sendbuf, strlen(sendbuf), 0);

            // sleep(1);

            // strcpy(sendbuf, "expostarted-2");
            // send(new_fd, sendbuf, strlen(sendbuf), 0);
            
            startExposure(arg);

        }
        else if( strcmp(recvbuf, "stop\n") == 0 ){
            
            abortExposure(arg);
            
        }
        else if( strcmp(recvbuf, "exit\n") == 0 ){
            // char stmp[256];
            // sprintf(stmp, "close connection from socket_fd: %d\n", new_fd);
            // send(new_fd, stmp, sizeof(stmp), 0);
            send(new_fd, "connection close.\n", sizeof("connection close.\n"), 0);

            break;
        }

        // if(strcmp(recvbuf,"exit\n") == 0){
        //     send(new_fd, "connection close.\n", sizeof("connection close.\n"), 0);
        //     break;
        // }

        send(new_fd, sendbuf, sizeof(sendbuf), 0);
        memset(recvbuf,0,sizeof(recvbuf));
    }

    close(pinfo->fd);
    pinfo->fd = -1;

    close(new_fd); //评论中指出多了一次close，所以这里不close了，留待main函数中统一close
    // pthread_exit(0);

    fprintf(stdout, "(%s) closing thread: %d\n",__func__, pthread_self());

    return NULL;
}

void startExposure(void *arg) {
    
    pthread_t ctid = pthread_self();
    fprintf(stdout, "[THREAD: %lu] exposure start ...\n", ctid);
    
    global_abort_exposure = 0;

    fprintf(stdout, "start exposure for 5s ...\n");

    int t_cnt = 0;
    while(t_cnt < 5){
        t_cnt ++;
        fprintf(stdout, "t = %d\n", t_cnt);
        sleep(1);
        
        if( global_abort_exposure == 1 ){
            fprintf(stdout, "[THREAD: %lu] exposure aborted ...\n", ctid);
            break;
        }
    }

    if( global_abort_exposure == 0 ){
        fprintf(stdout, "[THREAD: %lu] exposure finish ...\n", ctid);
    }

    // return NULL;
}


void abortExposure(void *arg){

    pthread_t ctid = pthread_self();
    global_abort_exposure = 1;
    fprintf(stdout, "[THREAD: %lu] send 'ABORT EXPOSURE' signal!\n", ctid);
}
